#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File     : Job.py
@Project  : dev
@Date     : 2021/12/5
@Author   : Zhang Jinyang
@Contact  : zhang-jy@sjtu.edu.cn
'''

import numpy as np

class Block:

    def __init__(self, job_id, block_id, target_core, start_time, finish_time, r):
        """
        记录各block的执行过程信息
        :param job_id:
        :param block_id:
        :param target_core:
        :param start_time:
        :param finish_time:
        :param r: 在core上的任务次序
        """
        self.job_id = job_id
        self.block_id = block_id
        self.target_core = target_core
        self.start_time = start_time
        self.finish_time = finish_time
        self.r = r
        self.host = 0
        self.job_start_time = 0

    def __str__(self):
        """
        用于打印结果
        """
        start_time = round(self.job_start_time+self.start_time,1)
        finish_time = round(self.job_start_time+self.finish_time,1)
        return f"\t\tJ{str(self.job_id).zfill(2)}, B{str(self.block_id).zfill(2)}, runTime {str(start_time).zfill(5)} to {str(finish_time).zfill(5)}\n"


class Job:

    def __init__(self, id, speed, blocks_size, bloc=None):
        """
        初始化id,block数量,执行速度,各block大小
        :param id:
        :param speed:
        :param blocks_size: 各block的大小
        :param bloc: 各block所在host
        """
        self.id = id
        self.speed = speed
        self.blocks_size = blocks_size
        self.block_num = len(blocks_size)
        self.total_size = sum(self.blocks_size)
        self.start_time = 0
        self.duration = None
        self.block_info = []
        if bloc != None:
            self.bloc = bloc
            self.bsize_per_host = {}
            for i, loc in enumerate(bloc):
                if loc not in self.bsize_per_host:
                    self.bsize_per_host[loc] = 0
                self.bsize_per_host[loc] += self.blocks_size[i]


    def execute(self, cores, alpha, core2host, tspeed):
        """
        Job内部执行函数，对于给定core集合，Job内部执行可用greedy算法确定
        :param cores: 执行所在的core集
        :param alpha:
        :param core2host: core到host映射，用于区别是否要加上传输时间
        :param tspeed: 传输速度，为0时表示单机，不计算传输时间
        :return:
        """
        self.cores = [i for i in cores]
        self.duration = np.zeros(len(cores))
        core2host = {i: core2host[c] for i, c in enumerate(cores)}
        g = 1 - alpha * (len(cores) - 1)
        block_order = np.argsort(self.blocks_size)
        self.block_per_core = [[] for _ in range(len(cores))]
        self.block_info = [0]*self.block_num
        for j in block_order[::-1]:
            target_core = -1
            min_F = float("inf")
            bsize = self.blocks_size[j]
            for i in range(len(cores)):
                cur_F = self.duration[i] + bsize / (self.speed * g)
                if self.bloc[j] != core2host[i] and tspeed != 0:
                    cur_F += bsize / tspeed

                if cur_F < min_F:
                    min_F = cur_F
                    target_core = i

            self.block_info[j] = Block(self.id, j, self.cores[target_core], round(self.duration[target_core],1),
                                       round(min_F,1), len(self.block_per_core[target_core]))
            self.duration[target_core] = min_F
            self.block_per_core[target_core].append(j)

        self.max_duration = np.max(self.duration)


    def outputSolutionFromBlock(self, core2host, block_per_core):

        mes = f"Job{self.id} obtains {len(self.cores)} cores(speed={self.speed}) " \
              f"and finishes at time {str(round(self.start_time + self.max_duration,1)).zfill(5)}:\n"
        fmt = "\tBlock{}: H{}, C{}, R{} (time={})\n"
        for i, blocks in enumerate(self.block_per_core):
            for j in blocks:
                h = core2host[self.block_info[j].target_core]
                self.block_info[j].r += len(block_per_core[self.block_info[j].target_core])
                mes += fmt.format(j, h, self.block_info[j].target_core, self.block_info[j].r, self.block_info[j].finish_time)
                block_per_core[self.block_info[j].target_core].append(self.block_info[j])
        return mes


    def update_block_info(self):
        """
        block在Job内部存在局部执行模式，将Job的全局执行时间赋值给block
        :return:
        """
        for block in self.block_info:
            block.job_start_time = self.start_time